
#include <stddef.h>
#include <assert.h>

#include "raise.h"
#include "class.h"

Object const* up_cast(Object const* object, MetaClass const* search_for)
{
    assert(object != NULL);
    assert(search_for != NULL);
    Class const* instance = (Class const*) object;
    assert(search_for != NULL);
    MetaClass const* tmp = typeof(object);
    while (tmp != NULL && search_for != tmp)
    {
        //printf("Skipping class '%s':  +%u bytes\n ", tmp->__name__, tmp->__size__);
        instance = (Class const*) (((char*) instance) + (ptrdiff_t) tmp->__size__);
        tmp = get_parent_type(tmp);
    }
    if (tmp == NULL)
        raise("Cannot convert instance of '", typeof(object)->__name__,"' into a '", search_for->__name__, "'");
    return  instance;
}

char const* str(Object const* object)
{
    assert(object != NULL && "Calling str() on NULL instance");

    MetaClass const* tmp = typeof(object);
    assert(tmp != NULL && "Instance is not valid (NULL metaclass)");
    while (tmp != NULL)
    {
        if (tmp->__str__ != NULL)
            return tmp->__str__(up_cast(object, tmp));
        tmp = (*tmp->__base__)->__metaclass__;
    }
    return typeof(object)->__name__;
}

#define find_type_method(o, m) \
({ \
    MetaClass const* _t = typeof(o); \
    while(_t != NULL && _t->m == NULL) \
        _t = (*_t->__base__)->__metaclass__; \
    _t;\
})



#define cls_op(op) \
Object* op(Object const* o1, Object const* o2) \
{ \
    assert(o1 != NULL); \
    assert(o2 != NULL); \
    assert(typeof(o1) == typeof(o2)); \
    MetaClass const* tmp = find_type_method(o1, __##op##__); \
    if (tmp == NULL || tmp->__##op##__ == NULL) \
        raise("No '" #op "' operator defined for class '", typeof(o1)->__name__, "'"); \
    return tmp->__##op##__(up_cast(o1, tmp), \
                           up_cast(o2, tmp)); \
}

cls_op(add)
cls_op(sub)
cls_op(mul)
cls_op(odiv)

